package proxy

import (
	//"bytes"
	//"encoding/json"

	"bytes"
	"encoding/json"
	"os"

	"gopkg.in/yaml.v2"

	//"bufio"
	//"bytes"
	"crypto/tls"
	"fmt"
	"io/ioutil"

	//"io/ioutil"

	//"encoding/json"

	//"encoding/json"
	//"fmt"
	"io"
	//"io/ioutil"
	"log"
	"mock/utils"
	"mock/write"
	"net"
	"net/http"
	"net/url"
	"strings"
)

type HttpProxy struct {
}

type Generate struct {
	ExcluedeHeader []string `json:"excluede_header,omitempty" yaml:"excluede_header,omitempty"`
	GenerateRule   []struct {
		DenyMethod []string `json:"deny_method,omitempty" yaml:"deny_method,omitempty"`
		Host       string   `json:"host,omitempty" yaml:"host,omitempty"`
	} `yaml:"generate_rule"`
	ExcluedeVerify []string `json:"excluede_verify,omitempty" yaml:"excluede_verify,omitempty"`
}

type Config struct {
	Name      string
	GlobalVar map[string]interface{}
}

var (
	GenerateCfg = Generate{}
)

// DoRequest 执行HTTP请求，并调用responseFunc处理response
func (p *HttpProxy) Https(req *http.Request, reqCon net.Conn) {

	newReq := new(http.Request)
	*newReq = *req
	newReq.Header = CloneHeader(newReq.Header)
	removeConnectionHeaders(newReq.Header)
	for _, item := range hopHeaders {
		if newReq.Header.Get(item) != "" {
			newReq.Header.Del(item)
		}
	}
	transport := http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}

	resp, err := transport.RoundTrip(newReq)
	if err != nil {
		log.Println(err)
		return
	}
	//for key, value := range resp.Header {
	//	for _, v := range value {
	//		rw.Header().Add(key, v)
	//	}
	//}

	err = resp.Write(reqCon)
	if err != nil {
		log.Println(err)
	}
	resp.Body.Close()
}

var hopHeaders = []string{
	"Connection",
	"Proxy-Connection",
	"Keep-Alive",
	"Proxy-Authenticate",
	"Proxy-Authorization",
	"Te",
	"Trailer",
	"Transfer-Encoding",
	"Upgrade",
}

func removeConnectionHeaders(h http.Header) {
	if c := h.Get("Connection"); c != "" {
		for _, f := range strings.Split(c, ",") {
			if f = strings.TrimSpace(f); f != "" {
				h.Del(f)
			}
		}
	}
}

func CloneHeader(h http.Header) http.Header {
	h2 := make(http.Header, len(h))
	for k, vv := range h {
		vv2 := make([]string, len(vv))
		copy(vv2, vv)
		h2[k] = vv2
	}
	return h2
}

//判断元素在不在列表
func inList(l []string, s string) bool {
	for _, v := range l {
		if strings.ToLower(v) == strings.ToLower(s) {
			return true
		}
	}
	return false
}

//判断是否需要把请求写入yaml
func isWriteYaml(host, method string) bool {
	for _, y := range GenerateCfg.GenerateRule {
		//判断请求的host，方法是否需要存入yaml
		//这里的方法是拒绝，如果存在即拒绝，所以取反
		if host == y.Host && !inList(y.DenyMethod, method) {
			return true
		}
	}
	return false
}

func (p *HttpProxy) Http(rw http.ResponseWriter, req *http.Request) {
	iswrite := isWriteYaml(req.Host, req.Method)
	dir := ""
	if iswrite {
		//通过:分割host,然后通过_再连接,example: test.cn:8090   test.cn_8090
		dir = strings.Join(strings.Split(req.Host, ":"), "_")
		//判断dir是否存在,不存在则创建
		_, err := os.Stat("./" + dir)
		if err != nil {
			log.Println(err, "没有目录，即将创建")
			if os.IsNotExist(err) {
				os.Mkdir(dir, os.ModePerm)
			}
		}
		//判断方法目录是否存在，不存在则创建
		_, err = os.Stat("./" + dir + "/" + req.Method)
		if err != nil {
			log.Println(err)
			if os.IsNotExist(err) {
				err := os.Mkdir("./"+dir+"/"+req.Method, os.ModePerm)
				log.Println(err)
			}
		}
	}

	transport := http.DefaultTransport
	outReq := new(http.Request)
	*outReq = *req
	//填充请求信息
	// teststep := write.Steps{}

	test := []write.Tests{}
	wr := write.Tests{}
	wr.Test.Request.Method = req.Method
	wr.Test.Name = req.URL.Path
	wr.Test.Request.Url = req.URL.Scheme + "://" + req.URL.Host
	wr.Test.Request.Path = req.URL.Path
	m := make(map[string]string)

	for key, _ := range req.Header {
		//填充header信息,排除过滤的
		if !inList(GenerateCfg.ExcluedeHeader, key) {
			m[key] = req.Header.Get(key)
		}
		//wr.Request.Headers=append(wr.Request.Headers,write.Item{Key:key,Value:req.Header.Get(key)})
		//wr.Request.Headers= append(wr.Request.Headers,map[string]string{key:req.Header.Get(key)})
	}
	wr.Test.Request.Headers = m

	//处理query参数
	if req.URL.RawQuery != "" {
		m := make(map[string]string)
		query := strings.Split(req.URL.RawQuery, "&")

		for _, item := range query {
			kv := strings.Split(item, "=")
			if len(kv) > 1 {
				t, _ := url.QueryUnescape(kv[1])
				m[kv[0]] = utils.Replace(t)
			} else {
				m[kv[0]] = ""
			}
		}

		// for _, item := range query {
		// 	value := strings.Split(item, "=")
		// 	m[value[0]] = value[1]
		// 	//wr.Request.Param=append(wr.Request.Param,write.Item{Key:value[0],Value:value[1]})
		// }
		wr.Test.Request.Param = m
	}

	//req.body的信息被读取后，再次读取时没有内容的，所以又重新把内容回填进去了
	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		log.Println(err)
	}
	outReq.Body = ioutil.NopCloser(bytes.NewReader(body))

	//定义一个空接口对象,把body中的数据转换成json对象

	//同理把请求信息填充进去

	//如果请求中的信息为json
	if strings.Contains(req.Header.Get("Content-Type"), "application/json") {
		var c interface{}
		json.Unmarshal(body, &c)
		wr.Test.Request.Body = c
	} else if strings.Contains(req.Header.Get("Content-Type"), "application/x-www-form-urlencoded") {
		var c interface{}
		err := json.Unmarshal(body, &c)
		if err != nil {
			fmt.Println(err)
			wr.Test.Request.Body = string(body)
		} else if err == nil {
			wr.Test.Request.Body = c
		}

	}

	if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
		if prior, ok := outReq.Header["X-Forwarded-For"]; ok {
			clientIP = strings.Join(prior, ", ") + ", " + clientIP
		}
		outReq.Header.Set("X-Forwarded-For", clientIP)
	}

	res, err := transport.RoundTrip(outReq)
	if err != nil {
		//访问目标服务失败
		log.Println(err)
		rw.WriteHeader(http.StatusBadGateway)
		return
	}

	// step 3
	//fmt.Println(res.Request.Method,res.Request.RequestURI,res.StatusCode)
	for key, value := range res.Header {
		for _, v := range value {
			rw.Header().Add(key, v)
		}
	}
	//读取响应内容
	resdata, err := ioutil.ReadAll(res.Body)
	if err != nil {
		log.Println(err)
	}
	rw.WriteHeader(res.StatusCode)
	io.Copy(rw, bytes.NewReader(resdata))
	res.Body.Close()

	var resinterface interface{}
	err = json.Unmarshal(resdata, &resinterface)
	if err != nil {
		log.Println(err)
	}
	unknow, ok := resinterface.(map[string]interface{})
	if ok && iswrite {
		for k, v := range unknow {
			if !inList(GenerateCfg.ExcluedeVerify, k) {
				var params []interface{}
				switch v2 := v.(type) {
				case string, float64, bool:
					params = append(params, "${content['"+k+"']}")
					params = append(params, v)
					wr.Test.Validate = append(wr.Test.Validate, write.Verify{Logic: "", Action: "eq", Params: params})
				// case []interface{}:
				// log.Println(k, "is []map", v2)  不写入，避免影响性能
				default:
					log.Println(k, "is another type not hand yet", v2)

				}
			}

		}
	}
	test = append(test, wr)
	// teststep.Step = test

	a, err := yaml.Marshal(test)
	if err != nil {
		log.Println(err, req.URL)
	}

	// b, err := yaml.Marshal(teststep)
	// if err != nil {
	// 	log.Println(err, req.URL)
	// }

	if iswrite {
		filename := "./" + dir + "/" + req.Method + "/" + strings.Join(strings.Split(strings.Trim(req.URL.Path, "/"), "/"), "_") + ".yaml"
		//当文件不存在的时候，写入一些信息，意思就是只写入一次，后面不写入
		_, err := os.Stat(filename)
		if err != nil {
			f, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0644)
			if err != nil {
				log.Println(err)
			}
			c, _ := yaml.Marshal(Config{Name: "testcase description"})
			f.Write(c)
			_, e := f.Write([]byte("api:\n"))
			if e != nil {
				log.Println(e)
			}
			f.Close()
		}
		f, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0644)
		if err != nil {
			log.Println(err)
		}
		_, e := f.Write(a)
		if e != nil {
			log.Println(e)
		}
		f.Close()
	}

}
